#encoding:utf-8
'''
Created on 2015年7月5日

@author: LWD
'''

"""
A multi-producer, multi-consumer dict (HashTable).
键 值一一对应，出现冲突，取最新值
即：键值冲突，更新值；否则添加键值
"""

from time import time as _time
import threading

__all__ = ['HashTable']

class HashTable:
    """Create a HashTable object with a given maximum size.

    If maxsize is <= 0, the queue size is infinite.
    """
    def __init__(self, maxsize=0):
        self.maxsize = maxsize
        self._init(maxsize)
        # mutex must be held whenever the HashTable is mutating.  All methods
        # that acquire mutex must release it before returning.  mutex
        # is shared between the three conditions, so acquiring and
        # releasing the conditions also acquires and releases mutex.
        self.mutex = threading.Lock()
        # Notify not_empty whenever an item is added to the HashTable; a
        # thread waiting to get is notified then.
        self.not_empty = threading.Condition(self.mutex)
        # Notify not_full whenever an item is removed from the HashTable;
        # a thread waiting to put is notified then.
        self.not_full = threading.Condition(self.mutex)
        self.minKeyValue = ("", 0)

    def dsize(self):
        """Return the approximate size of the HashTable (not reliable!)."""
        self.mutex.acquire()
        n = self._dsize()
        self.mutex.release()
        return n

    def empty(self):
        """Return True if the HashTable is empty, False otherwise (not reliable!)."""
        self.mutex.acquire()
        n = not self._dsize()
        self.mutex.release()
        return n

    def full(self):
        """Return True if the HashTable is full, False otherwise (not reliable!)."""
        self.mutex.acquire()
        if self.maxsize > 0:
            n = 0 < self.maxsize == self._qsize()
        else:
            n = False
        self.mutex.release()
        return n

    def Insert(self, item):
        """Put an item into the HashTable.

        put an item on the HashTable if a free slot
        is immediately available, else if the value larger than the minKeyValue
        then replace it.
        """
        self.not_full.acquire()
        try:
            if item[0] in self.dict:
                self.dict[item[0]] -= 1
                if self.dict[item[0]] < self.minKeyValue[1]:
                    self.minKeyValue = (item[0], self.dict[item[0]])
                return True 
            
            if self.maxsize > 0:
                if self._dsize() == self.maxsize:
                    # 如果满了，则判断新插入的是否比minKeyValue大，
                    # 如果大，则删掉minKeyValue，插入新值
                    if hasattr(self, "minKeyValue") and item[1] > self.minKeyValue[1]:
                        self._del(self.minKeyValue[0])
                        self.minKeyValue = item
                        # 查找更新最小元祖
                        for key in self.dict:
                            if self.dict[key] < self.minKeyValue[1]:
                                self.minKeyValue = (key, self.dict[key])
                    else:
                        # 没有添加到dict中
                        return False 
                else:
                    # 如果没有满 ，则判断新插入的是否比 minKeyValue 小，
                    # 如果小，则更显minKeyValue
                    if item[1] < self.minKeyValue[1]:
                        self.minKeyValue = item
            
            self._put(item)
            self.not_empty.notify()
            return True
        finally:
            self.not_full.release()
    
    def isExist(self, key):
        """
        """
        self.not_empty.acquire()
        try:
            item = self._get(key)
            if item == None:
                return False
            return True
        finally:
            self.not_empty.release()

    # Override these methods to implement HashTable
    # These will only be called with appropriate locks held

    # Initialize the dict representation
    def _init(self, maxsize):
        self.dict = {}

    def _dsize(self, len=len):
        return len(self.dict)

    # Put a new item in the dict
    def _put(self, item):
        (key, value) = item
        self.dict[key] = value

    # Get an item from the dict
    def _get(self, key):
        if key in self.dict:
            return self.dict[key]
        return None
    
    def _del(self, key):
        if key in self.dict:
            del self.dict[key]
            return True
        return False

